﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Windows.Media.Imaging;

namespace WallpaperCreatorForSamsungPhone
{
    /// <summary>
    /// Represents active image control.
    /// </summary>
    public partial class ActiveImageControl : UserControl
    {

        /// <summary>
        /// Gets or sets the angle.
        /// </summary>
        /// <value>The angle.</value>
        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set 
            { 
                SetValue(AngleProperty, value);

                imageRotateTransform.Angle = value;
                apRotateTransform.Angle = value;
            }
        }

        // Using a DependencyProperty as the backing store for Angle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AngleProperty =
            DependencyProperty.Register("Angle", typeof(double), typeof(ActiveImageControl), new PropertyMetadata((double)0));



        /// <summary>
        /// Gets or sets the scale.
        /// </summary>
        /// <value>The scale.</value>
        public double Scale
        {
            get { return (double)GetValue(ScaleProperty); }
            set 
            { 
                SetValue(ScaleProperty, value); 
                imageScale.ScaleX = imageScale.ScaleY = value;
            }
        }

        // Using a DependencyProperty as the backing store for Scale.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ScaleProperty =
            DependencyProperty.Register("Scale", typeof(double), typeof(ActiveImageControl), new PropertyMetadata((double)1));




        /// <summary>
        /// Gets or sets the offset.
        /// </summary>
        /// <value>The offset.</value>
        public Point Offset
        {
            get { return (Point)GetValue(OffsetProperty); }
            set 
            { 
                SetValue(OffsetProperty, value);

                imageTransform.X = value.X;
                imageTransform.Y = value.Y;
            }
        }

        // Using a DependencyProperty as the backing store for Offset.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty OffsetProperty =
            DependencyProperty.Register("Offset", typeof(Point), typeof(ActiveImageControl), null);


        /// <summary>
        /// Initializes a new instance of the <see cref="ActiveImageControl"/> class.
        /// </summary>
        public ActiveImageControl()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Handles the Drop event of the Image control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.DragEventArgs"/> instance containing the event data.</param>
        private void Image_Drop(object sender, DragEventArgs e)
        {
            Image image = (Image)sender;

            if (e.Data != null)
            {
                FileInfo[] files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];

                if (files != null)
                    LoadImageFromFile(image, files);
            }

        }

        /// <summary>
        /// Loads the image from file.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="files">The files.</param>
        private void LoadImageFromFile(Image image, params FileInfo[] files)
        {
            if (image == null)
                throw new ArgumentNullException("image");

            foreach (FileInfo file in files)
            {
                if (file.Exists)
                {
                    try
                    {
                        using (Stream stream = file.OpenRead())
                        {
                            // Decode the image bits
                            BitmapImage bi = new BitmapImage();
                            bi.SetSource(stream);

                            image.Source = bi;

                            FitImageToControl(bi.PixelWidth, bi.PixelHeight);

                            break;
                        }
                    }
                    catch (Exception)
                    {
                    }
                }
            }
        }

        /// <summary>
        /// Fits the image to control.
        /// </summary>
        /// <param name="pixelWidth">Width of the pixel.</param>
        /// <param name="pixelHeight">Height of the pixel.</param>
        private void FitImageToControl(int pixelWidth, int pixelHeight)
        {
            this.Angle = 0;

            // Scale Fit by Height
            this.Scale = this.RenderSize.Height / pixelHeight;

            // Center
            this.Offset = new Point(this.Scale*(this.RenderSize.Width - pixelWidth) / 2,
                this.Scale*(this.RenderSize.Height - pixelHeight) / 2);
        }

        /// <summary>
        /// Handles the MouseMove event of the Image control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void Image_MouseMove(object sender, MouseEventArgs e)
        {
            if (ImageMoveCaptured)
            {
                Point newMousePoint = e.GetPosition(this);

                imageTransform.X = this.PrevOffset.X - (this.MoveMouseClickPoint.X - newMousePoint.X);
                imageTransform.Y = this.PrevOffset.Y - (this.MoveMouseClickPoint.Y - newMousePoint.Y);
            }
            else
            if (ShowTransformWhell())
            {
                if (!this.ActionPointCaptured && !this.ImageMoveCaptured)
                {
                    showTransformationWheel.Begin();
                }
            }
        }

        /// <summary>
        /// Shows the transform whell.
        /// </summary>
        private bool ShowTransformWhell()
        {
            bool bRetVal = freeTransformWhell.Visibility != System.Windows.Visibility.Visible;

            freeTransformWhell.Visibility = System.Windows.Visibility.Visible;
            actionPoint.Visibility = System.Windows.Visibility.Visible;
            topMenu.Visibility = System.Windows.Visibility.Visible;

            angleTextBlock.Visibility = System.Windows.Visibility.Visible;
            zoomTextBlock.Visibility = System.Windows.Visibility.Visible;

            if (hideTransformationWheel.GetCurrentState() == ClockState.Active)
                hideTransformationWheel.Stop();


            return bRetVal;
        }

        /// <summary>
        /// Handles the MouseLeave event of the Image control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void Image_MouseLeave(object sender, MouseEventArgs e)
        {
            if (!ImageMoveCaptured)
                HideTransformWhell();
        }

        /// <summary>
        /// Hides the transform whell.
        /// </summary>
        private void HideTransformWhell()
        {
            hideTransformationWheel.Begin();
        }

        /// <summary>
        /// Handles the Completed event of the hideTransformationWheel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void hideTransformationWheel_Completed(object sender, EventArgs e)
        {
            freeTransformWhell.Visibility = System.Windows.Visibility.Collapsed;
            actionPoint.Visibility = System.Windows.Visibility.Collapsed;
            topMenu.Visibility = System.Windows.Visibility.Collapsed;

            angleTextBlock.Visibility = System.Windows.Visibility.Collapsed;
            zoomTextBlock.Visibility = System.Windows.Visibility.Collapsed;
        }

        /// <summary>
        /// Gets or sets the source.
        /// </summary>
        /// <value>The source.</value>
        public ImageSource Source 
        { 
            get
            {
                return sourceImage.Source;
            }
            set
            {
                sourceImage.Source = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [action point capture].
        /// </summary>
        /// <value><c>true</c> if [action point capture]; otherwise, <c>false</c>.</value>
        private bool ActionPointCaptured { get; set; }
        /// <summary>
        /// Gets or sets the prev angle.
        /// </summary>
        /// <value>The prev angle.</value>
        private double PrevAngle { get; set; }
        /// <summary>
        /// Gets or sets the prev scale.
        /// </summary>
        /// <value>The prev scale.</value>
        private double PrevScale { get; set; }

        /// <summary>
        /// Gets or sets the prev offset.
        /// </summary>
        /// <value>The pref offset.</value>
        public Point PrevOffset { get; set; }

        /// <summary>
        /// Handles the MouseLeftButtonDown event of the actionPoint control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void actionPoint_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;

            ActionPointCaptured = true;
            actionPoint.CaptureMouse();

            this.PrevAngle = this.Angle;
            this.PrevScale = this.Scale;
        }

        /// <summary>
        /// Handles the MouseMove event of the actionPoint control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void actionPoint_MouseMove(object sender, MouseEventArgs e)
        {
            if (ActionPointCaptured)
            {
                Point mousePoint = e.GetPosition(this);

                // Calculate a new Angle and Scale
                double xOffset = (mousePoint.X - this.ActualWidth / 2);
                double yOffset = (mousePoint.Y - this.ActualHeight/ 2);

                double prevAngle = this.Angle;

                double len = Math.Sqrt(xOffset * xOffset + yOffset * yOffset);

                this.Angle = Math.Asin(yOffset / len) * 180/ Math.PI;

                if (xOffset < 0)
                    this.Angle = 180 - this.Angle;
                if (this.Angle < 0)
                    this.Angle += 360;

                double prevScale = this.Scale;
                this.Scale = len * this.PrevScale / 60;

                if (this.Scale < double.Epsilon)
                    this.Scale = double.Epsilon;

                // Stick TransformAngle
                if (prevAngle % 15 == 0 && Math.Abs(this.Angle - prevAngle) < 8)
                    this.Angle = prevAngle;

                // Stick TransformScale
                if (prevScale % 25 == 0 && Math.Abs(this.Scale - prevScale) < 15)
                    this.Scale = prevScale;
            }
        }

        /// <summary>
        /// Handles the MouseLeftButtonUp event of the actionPoint control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void actionPoint_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (ActionPointCaptured)
            {
                e.Handled = true;

                ActionPointCaptured = false;
                actionPoint.ReleaseMouseCapture();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [image move captured].
        /// </summary>
        /// <value><c>true</c> if [image move captured]; otherwise, <c>false</c>.</value>
        private bool ImageMoveCaptured { get; set; }

        /// <summary>
        /// Gets or sets the move mouse click point.
        /// </summary>
        /// <value>The move mouse click point.</value>
        private Point MoveMouseClickPoint { get; set; }

        /// <summary>
        /// Handles the MouseLeftButtonDown event of the freeTransformWhell control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //HideTransformWhell();

            if (e.OriginalSource == movePoint)
            {
                e.Handled = false;
            }
            else
            {
                e.Handled = true;

                ImageMoveCaptured = true;
                LayoutRoot.CaptureMouse();

                this.MoveMouseClickPoint = e.GetPosition(this);
                this.PrevOffset = new Point(imageTransform.X, imageTransform.Y);
            }
        }

        /// <summary>
        /// Handles the MouseLeftButtonUp event of the freeTransformWhell control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void Image_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (ImageMoveCaptured)
            {
                e.Handled = true;

                ImageMoveCaptured = false;
                LayoutRoot.ReleaseMouseCapture();
            }
        }

        /// <summary>
        /// Handles the SizeChanged event of the LayoutRoot control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.SizeChangedEventArgs"/> instance containing the event data.</param>
        private void imageCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            RectangleGeometry rg = new RectangleGeometry();
            rg.Rect = new Rect(new Point(0,0), e.NewSize);

            LayoutRoot.Clip = rg;

            imageScale.CenterX = e.NewSize.Width / 2;
            imageScale.CenterY = e.NewSize.Height / 2;

            imageRotateTransform.CenterX = e.NewSize.Width / 2;
            imageRotateTransform.CenterY = e.NewSize.Height / 2;
        }

        /// <summary>
        /// Handles the MouseLeftButtonDown event of the openImage control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void openImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;

            OpenFileDialog openFile = new OpenFileDialog();
            openFile.Multiselect = false;
            openFile.Filter = "Jpeg files|*.jpg|Bmp files|*.bmp|All files|*.*";

            bool? bResult = openFile.ShowDialog();

            if (bResult.HasValue && bResult.Value)
            {
                LoadImageFromFile(sourceImage, openFile.File);
            }
        }

        /// <summary>
        /// Handles the MouseLeftButtonDown event of the fitImage control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void fitImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;

            if (this.Source is BitmapImage)
            {
                BitmapImage bi = (BitmapImage)this.Source;

                FitImageToControl(bi.PixelWidth, bi.PixelHeight);
            }
        }

 }
}
